//------------------------------------------------------------------------
// Abstract: Implement of class Memory
//
// Authoring: XiaoHanxiong
//
// Time: 2013.12.30
//
// Static test:
//
// Version history:
//		Ver_1.0 : 2013.12.30	XiaoHanxiong
//------------------------------------------------------------------------
#include "Stdafx.h"
#include "Memory.h"

namespace Library
{
	Memory::Memory()
	{
		Initialize();
		Allocate(MCS_BASIC_ALLOCATION, MCS_BASIC_ALLOCATION);
	}

	Memory::Memory( const Memory& memory )
	{
		Copy(memory);
	}

	Memory::~Memory()
	{
		if(m_base != NULL)
		{
			delete [] m_base;
		}
	}

	void Memory::Initialize()
	{
		memset(this, 0, sizeof(Memory));
	}

	void Memory::Release()
	{
		if(m_base != NULL)
			delete [] m_base;
		Initialize();
	}

	void Memory::Allocate( size_t front, size_t back )
	{
		UINT8* old_base = m_base;
		UINT8* new_base = NULL;

		// If the input size is all less than before, return directly
		if(front <= m_front_size && back <= m_back_size)
			return;

		// Calculate the actual size to ensure both size is not less than before
		front = (front > m_front_size) ? front : m_front_size;
		back = (back > m_back_size) ? back : m_back_size;

		new_base = new UINT8[front + back]();
		memset(new_base, 0, front + back);


		UINT8* old_zero = m_zero;
		m_base = new_base;
		m_zero = m_base + front;

		m_front = m_zero - (old_zero - m_front);
		m_back = m_zero + (m_back - old_zero);

		if(old_base)
		{
			memcpy(m_zero - m_front_size, old_base, m_front_size);
			memcpy(m_zero, old_zero, m_back_size);
			delete [] old_base;		
		}

		m_front_size = front;
		m_back_size = back;
	}

	void Memory::Copy( const Memory& memory )
	{
		if(this == &memory)
			return;

		Release();
		Allocate(memory.m_front_size, memory.m_back_size);
		memcpy(this->m_base, memory.m_base, m_front_size + m_back_size);

		m_zero = m_base + m_front_size;
		m_front = m_zero - (memory.m_zero - memory.m_front);
		m_back = m_zero + (memory.m_back - memory.m_zero);
	}

	void Memory::PushBack( UINT8 value )
	{
		if(m_zero + m_back_size == m_back)
			Allocate(m_front_size, m_back_size * 2);

		*(m_back++) = value;
	}

	void Memory::PushBack( UINT16 value )
	{
		if(m_zero + m_back_size == m_back + 1)
			Allocate(m_front_size, m_back_size * 2);

		*(UINT16*)(m_back) = value;
		m_back += 2;
	}

	void Memory::PushBack( UINT32 value )
	{
		if(m_zero + m_back_size == m_back + 3)
			Allocate(m_front_size, m_back_size * 2);

		*(UINT32*)(m_back) = value;
		m_back += 4;
	}

	void Memory::PushBack( float value )
	{
		if(m_zero + m_back_size == m_back + 3)
			Allocate(m_front_size, m_back_size * 2);

		*(float*)(m_back) = value;
		m_back += 4;
	}

	void Memory::PushBack( double value )
	{
		if(m_zero + m_back_size == m_back + 7)
			Allocate(m_front_size, m_back_size * 2);

		*(double*)(m_back) = value;
		m_back += 8;
	}

	void Memory::PushBack( void* value, size_t size )
	{
		size_t remian = m_back_size - (m_back - m_zero);
		if(remian <= size)
		{
			size_t increase_size = m_back_size;
			while(remian <= size)
			{
				increase_size *= 2;
				remian = increase_size - (m_back - m_zero);
			}
			Allocate(m_front_size, increase_size);
		}
		memcpy(m_back, value, size);
		m_back += size;
	}

	void Memory::PushFront( UINT8 value )
	{
		if(m_zero - m_front_size == m_front)
			Allocate(m_front_size * 2, m_back_size);

		*(--m_front) = value;
	}

	void Memory::PushFront( UINT16 value )
	{
		if(m_zero - m_front_size == m_front + 1)
			Allocate(m_front_size * 2, m_back_size);

		m_front -= 2;
		*(UINT16*)(m_front) = value;
	}

	void Memory::PushFront( UINT32 value )
	{
		if(m_zero - m_front_size == m_front + 3)
			Allocate(m_front_size * 2, m_back_size);

		m_front -= 4;
		*(UINT32*)(m_front) = value;
	}

	void Memory::PushFront( float value )
	{
		if(m_zero - m_front_size == m_front + 3)
			Allocate(m_front_size * 2, m_back_size);

		m_front -= 4;
		*(float*)(m_front) = value;
	}

	void Memory::PushFront( double value )
	{
		if(m_zero - m_front_size == m_front + 7)
			Allocate(m_front_size * 2, m_back_size);

		m_front -= 8;
		*(double*)(m_front) = value;
	}

	void Memory::PushFront( void* value, size_t size )
	{
		size_t remian = m_front_size - (m_zero - m_front);
		if(remian <= size)
		{
			size_t increase_size = m_front_size;
			while(remian <= size)
			{
				increase_size *= 2;
				remian = increase_size - (m_zero - m_front);
			}
			Allocate(increase_size, m_back_size);
		}
		m_front -= size;
		memcpy(m_front, value, size);
	}

	UINT8 Memory::Get( size_t index )
	{
		UINT8* address = m_front + index; 
		if(address < m_back )
		{
			return(*address);
		}
		else return(0);
	}

	void Memory::Set( size_t index, UINT8 value )
	{
		UINT8* address = m_front + index; 
		if(address < m_back )
		{
			*address = value;
		}
	}

	size_t Memory::Size()
	{
		return(size_t)(m_back - m_front);
	}

	void Memory::Increase( size_t front, size_t back )
	{
		size_t resize_front = 0;
		size_t resize_back = 0;

		while(front > resize_front)
		{
			resize_front *= 2;
		}

		while(back > resize_back)
		{
			resize_back *= 2;
		}

		resize_front = (resize_front > m_front_size) ? resize_front : m_front_size;
		resize_back = (resize_back > m_back_size) ? resize_back : m_back_size;
		Allocate(resize_front, resize_back);
	}

	size_t Memory::Capacity()
	{
		return (m_front_size + m_back_size);
	}

	const byte* Memory::Data()
	{
		return(m_front);
	}

	void Memory::Clear()
	{
		m_front = m_zero;
		m_back = m_zero;
		memset(m_base, 0, m_front_size + m_back_size);
	}

	Memory& Memory::operator = ( Memory& memory )
	{
		if(&memory == this)
			return(*this);
		Copy(memory);
		return(*this);
	}

	byte Memory::operator [] ( size_t index )
	{
		return Get(index);
	}

	Memory& Memory::operator += ( Memory& memory )
	{
		if(&memory == this)
			return(*this);

		size_t src_size = memory.Size();
		size_t remian = m_back_size - (m_back - m_zero);
		if(remian <= src_size)
		{
			size_t increase_size = m_back_size;
			while(remian <= src_size)
			{
				increase_size *= 2;
				remian = increase_size - (m_back - m_zero);
			}
			Allocate(m_front_size, increase_size);
		}
		memcpy(m_back, memory.m_front, src_size);
		m_back += src_size;

		return(*this);
	}

	const byte* Memory::Begin()
	{
		return m_front;
	}

	const byte* Memory::End()
	{
		return m_back;
	}

	const byte* Memory::Zero()
	{
		return m_zero;
	}
}// _Library_